package com.petecapra.cashflow.model;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.Digits;
import javax.validation.constraints.NotNull;

import org.hibernate.annotations.Cascade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.format.annotation.DateTimeFormat;

import com.petecapra.cashflow.model.dao.AccountTransactionManager;
import com.petecapra.cashflow.model.dao.ScheduleManager;
import com.petecapra.cashflow.utils.DateUtils;

@Entity
@Table(name="tblSchedules")
@Configurable
public class Schedule extends BaseDomainObject<ScheduleManager> implements Serializable {

	private static final long serialVersionUID = 1L;
	
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	private long id;

	@NotNull
    private String name;
	
	@NotNull
    @ManyToOne(targetEntity = Account.class)
    @JoinColumn
    private Account account;
	
	@NotNull
    @Enumerated(EnumType.STRING)
    private FrequencyType frequency;
	
	@NotNull
    @Enumerated(EnumType.STRING)
    private TransactionType transactionType;
	
    @NotNull    
    private int remainingOccurrences;
    
    @NotNull    
    @DateTimeFormat(style="S-")
    private Date start;
    
    @NotNull
    @Digits(integer = 7, fraction = 2)
    private Double amount;
    
    @OneToMany(cascade = CascadeType.ALL, mappedBy = "schedule")
    @Cascade(org.hibernate.annotations.CascadeType.DELETE_ORPHAN)
    private List<AccountTransaction> accountTransactions = new ArrayList<AccountTransaction>();
    
    @Transient
	@Autowired
	private AccountTransactionManager accountTransactionManager;
    
    @Transient
	@Autowired
	private ScheduleManager scheduleManager;
    
    public Schedule() {}
    
    public Schedule(Account account, String name, FrequencyType frequency, int occurrences, Double amount) {
    	this.account = account;
    	this.name = name;
    	this.frequency = frequency;
    	this.remainingOccurrences = occurrences;
    	this.amount = amount;
    	if (amount >= 0) {
    		this.transactionType = TransactionType.Income;
    	} else {
    		this.transactionType = TransactionType.Expense;
    	}
    	this.start = new Date();
    }
    
	public long getId() {
		return id;
	}

	public void setId(long id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Account getAccount() {
		return account;
	}

	public void setAccount(Account account) {
		this.account = account;
	}

	public FrequencyType getFrequency() {
		return frequency;
	}

	public void setFrequency(FrequencyType frequency) {
		this.frequency = frequency;
	}

	public TransactionType getTransactionType() {
		return transactionType;
	}

	public void setTransactionType(TransactionType transactionType) {
		this.transactionType = transactionType;
	}

	public int getRemainingOccurrences() {
		return remainingOccurrences;
	}

	public void setRemainingOccurrences(int remainingOccurrences) {
		this.remainingOccurrences = remainingOccurrences;
	}

	public void setStart(Date start) {
		this.start = start;
	}

	public Date getStart() {
		return start;
	}

	public Double getAmount() {
		return amount;
	}

	public void setAmount(Double amount) {
		this.amount = amount;
	}

	public List<AccountTransaction> getAccountTransactions() {
		return accountTransactions;
	}

	public void setAccountTransactions(List<AccountTransaction> accountTransactions) {
		this.accountTransactions = accountTransactions;
	}
	
	public AccountTransactionManager getAccountTransactionManager() {
		return accountTransactionManager;
	}

	public void setAccountTransactionManager(
			AccountTransactionManager accountTransactionManager) {
		this.accountTransactionManager = accountTransactionManager;
	}

	public void payTransaction() {
		account.addAmount(amount);
		remainingOccurrences = remainingOccurrences - 1;
	}
	
	public AccountTransaction addTransaction(Date due) {
		AccountTransaction at = new AccountTransaction(this, due);
		accountTransactions.add(at);
		accountTransactionManager.save(at);
		return at;
	}
	
	public void updateRecurrences() {
    	
    	// wipe account transactions
    	removeUnpaidTransactions();
    	
    	// if there is a recurrence then let's generate the others
    	if (!frequency.equals(FrequencyType.None) && remainingOccurrences > 1) {
    		
    		// create one for each date needed
    		for (Date reccurence: DateUtils.getFrequencyDates(frequency, start, remainingOccurrences)) {    			
    			addTransaction(reccurence);    			
    		}    		
    		
    	// there are no recurrences so let's just add one
    	} else {    		
    		addTransaction(start);  		
    	}
    	
    }
    
    public void removeUnpaidTransactions() {
    	for (AccountTransaction at: account.getUnpaidTransactions()) {    	
    		getAccountTransactions().remove(at);
    	}    	
    }

	@Override
	public ScheduleManager getManager() {
		return scheduleManager;
	}

	public void deleteTransaction(AccountTransaction at) {
		if (accountTransactions.contains(at)) {
			accountTransactionManager.delete(at);
			accountTransactions.remove(at);
			remainingOccurrences--;
			save();
		}		
	}
	
}